<?php
include_once BXD_INCLUDEPATH . 'lib/entity/EntityUtils.php';
include_once BXD_INCLUDEPATH . 'lib/database/Database.php';

/**
 * Classe per la gestione della paginazione. In questa classe attraverso i metodi
 * 'addSelectParam', 'addFromParam', 'addWhereParam', 'addOrderByParam' è possibile
 * costruire la query da eseguire attraverso il metodo 'start'. Quest'ultimo recupera
 * il numero di righe totali attreverso una COUNT e recupera i dati specificati nella
 * SELECT eseguendo una paginazione con l'istruzione LIMIT di MySql. Al termine del
 * processo viene restituito un array di oggetti (o un oggetto dinamico o la Entity
 * specificata nel costruttore della classe).
 *
 * @author Damiano Ciarla
 * @version 1.0
 * @category Lib
 */
final class Pagination
{
	/**
	 * Pagina di DEFAULT da cui iniziare la paginazione
	 */
	const DEF_PAGE		= 1;
	/**
	 * Numero di DEFAULT di righe da mostrare in ogni pagina
	 */
	const DEF_NUM_ROWS	= 10;

	private $entity	= NULL;

    private $select		= array();
	private $from		= array();
	private $where		= array();
	private $orderBy	= array();

	private $page		= NULL;
	private $numRows	= NULL;
	private $maxPage	= NULL;
	private $maxItems	= NULL;
	private $items		= array();

	/**
	 * Costruttore della classe che inizializza il nome della Entity nel quale
	 * caricare le informazioni recuperate dalla Query
	 *
	 * @param String $entity Nome della Entity
	 */
	
	public function __construct($entity = NULL)
	{
		$this->entity = $entity;
	}

	/**
	 * Imposta i campi selzionare nella SELECT della query. Se non viene passato
	 * alcun valore di default vengono presi tutti (*)
	 *
	 * @param String $param Nome del campo sulla tabella
	 */

	public function addSelectParam($param)
	{
		$this->select[] = $param;
	}

	/**
	 * Imposta le tabelle da cui devono assere presi i valori. Si possono inserire
	 * o solamenti i nome delle tabelle o anche fare le singole JOIN
	 *
	 * @param String $param Nome della tabella o JOIN
	 */

	public function addFromParam($param)
	{
		$this->from[] = $param;
	}

	/**
	 * Imposta le condizioni della SELECT. Si deve passare la stringa con il nome
	 * del campo e la relativa condizione. Es: 'id = 1'
	 *
	 * @param String $operator	Operatore per legare la condizione
	 * @param String $param		Condizione
	 */

	public function addWhereParam($operator, $param)
	{
		$this->where[] = array($operator, $param);
	}

	/**
	 * Imposta i valori su come deve essere ordinata la query. Es: 'name ASC'
	 *
	 * @param String $param Valore di ordinamento della query
	 */

	public function addOrderByParam($param)
	{
		$this->orderBy[] = $param;
	}

	/**
	 * Inizializza tutti i parametri eseguendo le query necessarie.
	 *
	 * @param int $page		Pagina corrente. Se NULL di default = 1
	 * @param int $numRows	Numero di righe per pagina. Se NULL di default = 10
	 */

	public function start($page = NULL, $numRows = NULL)
	{
		//--> Configurazione parametri inziali

		if($this->entity === NULL)
			include_once BXD_INCLUDEPATH . 'lib/entity/EntityCustom.php';

		if($page === NULL) 
			$page = self::DEF_PAGE;

		if($numRows === NULL) 
			$numRows = self::DEF_NUM_ROWS;

		//--> Creo la query per recuperare il numero totale dei Record che si possono estrarre

		$query =	"SELECT COUNT(*) AS COUNT_ROW " .
					"FROM " . $this->getFromString() . " " .
					"WHERE " . $this->getWhereString() . " ";

		Database::getInstance()->prepareQuery($query);
		Database::getInstance()->executeQuery();

		$result = Database::getInstance()->getArrayAssocResult();

		$count = $result['COUNT_ROW'];

		$this->page		= $page;
		$this->numRows	= $numRows;
		$this->maxPage	= ceil($count/$numRows);
		$this->maxItems	= $count;

		if($count > 0)
		{
			//--> Ricavo il valore dell'ordinamento
			$orderBy = count($this->orderBy) > 0 ? "ORDER BY " . $this->getOrderByString() : "";

			//--> Creo la query per recuperare le informazioni da passare alla Entity
			$query =	"SELECT " . $this->getSelectString() . " " .
						"FROM " . $this->getFromString() . " " .
						"WHERE " . $this->getWhereString() . " " . $orderBy . " " .
						"LIMIT " . (($page - 1) * $numRows) . ", " . $numRows;

			//--> Eseguo la query per recuperare le informazioni
			if (Database::getInstance()->prepareQuery($query))
			{
				if (Database::getInstance()->executeQuery() !== FALSE)
				{
					foreach(Database::getInstance()->getFetchAll() as $item)
					{
						if($this->entity !== NULL)
						{
							$this->items[] = EntityUtils::getInstance()->fillEntity(new $this->entity(), $item);
						}
						else
						{
							$entityCustom = new EntityCustom();

							foreach ($item as $column => $value)
								$entityCustom->$column = $value;

							$this->items[] = $entityCustom;
						}
					}
				}
				else
				{
					$error = Database::getInstance()->getLastError();
					BXD::newException("DatabaseError", $error[2]);
				}
			}
			else
			{
				$error = Database::getInstance()->getLastError();
				BXD::newException("DatabaseError", $error[2]);
			}
		}
	}

	/**
	 * Restituisce la pagina corrente
	 *
	 * @return int
	 */

	public function getPage()
	{
		return $this->page;
	}

	/**
	 * Restituisce il numero di righe da visualizzare per pagina
	 *
	 * @return int
	 */

	public function getNumRows()
	{
		return $this->numRows;
	}

	/**
	 * Restituisce il numero massimo di pagine
	 *
	 * @return int
	 */

	public function getMaxPage()
	{
		return $this->maxPage;
	}

	/**
	 * Restituisce il numero massimo di record selezionabili
	 *
	 * @return int
	 */

	public function getMaxItems()
	{
		return $this->maxItems;
	}

	/**
	 * Restituisce l'array di tutti gli oggetti
	 *
	 * @return array
	 */

	public function getItems()
	{
		return $this->items;
	}

	/**
	 * Metodo che ritorna la tipologia della Entity usata per memorizzare i dati restituiti dalla query
	 *
	 * @return String Tipo di entity
	 */

	public function getTypeEntity()
	{
		if($this->entity === NULL)
			return "custom";
		else
			return "object";
	}

	/*** -------------------- *** METODI INTERNI *** -------------------- ***/

	private function getSelectString()
	{
		if(count($this->select) == 0)
			$select = "*";
		else
		{
			$select = "";

			foreach ($this->select as $param)
			{
				$select .= $param;

				if($param != end($this->select))
					$select .= ", ";
			}
		}

		return $select;
	}

	private function getFromString()
	{
		$from = "";

		foreach ($this->from as $param)
		{
			$from .= $param;

			if($param != end($this->from))
				$from .= ", ";
		}

		return $from;
	}

	private function getWhereString()
	{
		$where = count($this->where) == 0 ? "true" : "";

		foreach ($this->where as $param)
			$where .= ($param[0] === NULL ? "" : " " . $param[0] . " ") . $param[1];

		return $where;
	}

	private function getOrderByString()
	{
		$orderBy = "";

		foreach ($this->orderBy as $param)
		{
			$orderBy .= $param;

			if($param != end($this->orderBy))
				$orderBy .= ", ";
		}

		return $orderBy;
	}
}
?>
